/**

This file is part of MaCI/GIMnet.

MaCI/GIMnet is free software: you can redistribute it and/or modify it 
under the terms of the GNU Lesser General Public License as published 
by the Free Software Foundation, either version 3 of the License, or 
(at your option) any later version.

MaCI/GIMnet is distributed in the hope that it will be useful, but WITHOUT 
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public 
License for more details.

You should have received a copy of the GNU Lesser General Public 
License along with GIMnet. (See COPYING.LESSER) If not, see 
<http://www.gnu.org/licenses/>.

**/
/**
 *
 * $Id: IOClient.hpp,v 1.7 2009-05-13 07:18:17 amaula Exp $
 *
 * \file
 * \brief MaCI - IO Interface Client header
 * \author Matthieu Myrsky <matthieu.myrsky@tkk.fi>
 *
 */
#ifndef _MACI_INTERFACE_IOCLIENT_HPP_
#define _MACI_INTERFACE_IOCLIENT_HPP_

#include "MaCI.hpp"
#include "IOData.hpp"
#include "thread.hpp"
#include "sync.hpp"
#include <map>



namespace MaCI {
  namespace IO {

    /** MaCI - IO Client interface.
     * 
     * This class contains the methods and types for using
     * a MaCI::IO service in the GIMnet.
     */
    class CIOClient : private gim::CSync,
                      private gim::CThread,
                      public MaCI::CMaCI
    {
    public:

      /** Constructor.
       * 
       * Constructor of the IOClient. This requires;
       * a pointer to Initialized and connected GIMI, minor
       * number of this interface (Used as GIMI minor number)
       *
       * @param[in] aGIMIPtr Pointer to GIMI used for communication
       * @param[in] aInterfaceMinor Interface minor number. Usually
       *            used to indicate the instance of devices of same
       *            type.
       */     
      CIOClient(gimi::GIMI *aGIMIPtr, const int aInterfaceMinor);

      
      /** Destructor.
       */
      ~CIOClient();
      

      /** Open client interface.
       */
      EMaCIError DoOpen(void);


      /** Close client interface.
       */
      EMaCIError DoClose(void);

      

      /**
       * Sets the wanted state to the wanted pin.
       *
       * @param[in] aPin                  The pin to set the value
       * @param[in] aState                The wanted state (True is HIGH
       *                                  false is LOW)
       * @return                          true on success, false on failure
       */ 
      bool SetDigitalPin(const int aPin, const bool aState);
      
      /**
       * Sets the wanted mask block (32 pins) to the digital pins. The block 
       * number means that the mask is applied to port bits starting from
       * (block number * 32). The mask is applied through a apply mask, which
       * decides which bits in the BlockState are really applied. (The bits
       * not selected to apply are left as-is on the port. This allows settings
       * multiple pins at port without interfering with possible other devices.
       *
       * @param[in] aBlock                The block number.
       * @param[in] aBlockState           The state of the pins
       * @param[in] aBlockStateApplyMask  the apply mask, 1 for pins to change
       * @return                          true on success, false on failure
       */    
      bool SetDigitalMask(const int aBlock, 
			  const int aBlockState,
			  const int aBlockStateApplyMask);
      
      /**
       * Returns the state of the wanted pin. The pin numbering is flattened,
       * so each IO card implementation should provide their own mapping for this.
       * This approach is selected because this allows diverse IO card hardware to be
       * used. 
       *
       * NOTE: This function is now capable of returning error value. (In case
       * of error though, false is returned)
       * 
       * @param[in] aPin                   The pin number
       * @param[in] aTimeout_ms            Time to wait for a command (milliseconds)
       * @return                           0 if the state is HIGH, 
       *                                   1 if the state is LOW.
       *                                   -1 when request sending failed or no response
       *                                   within timeout_ms
       */ 				    
      int GetDigitalPin(const int aPin, unsigned int aTimeout_ms);
      
      
      /**
       * Returns the state of the pins in block of 32 pins.
       *
       * NOTE: This function is now capable of returning error value. (In case
       * of error though, zero filled mask is returned)
       *
       * @param[in] aBlock                 The block number. This points to
       *                                   beginning of a 32 bit block of IO pins.
       *                                   if the card doesn't implement the whole
       *                                   32 pins, the unused bits are always returned
       *                                   as LOW states. (See the capabilities on
       *                                   how to query the real number of pins on card)
       * @param[in] aTimeout_ms            Time to wait for a command (milliseconds)
       * @return                           The 32 bit value representing the state
       *                                   of the selected block. (Each bit corresponds
       *                                   to a single pin) 
       *                                   OR
       *                                   -1 when request sending failed or no response
       *                                   within timeout_ms
       */
      int GetDigitalMask(const int aBlock, unsigned int aTimeout_ms);
      
      /**
       * Return the analog voltage of analog channel 'channel'. (This commands
       * the A/D converted on the IO device)
       *
       * NOTE: This function is now capable of returning error value. (In case
       * of error though, zero voltage is returned)
       *
       * @param[in] aChannel               The channel of analog input.
       * @param[in] aTimeout_ms            Time to wait for a command (milliseconds)
       * @return                           The analog voltage value of the wanted port.
       *                                   OR
       *                                   FLT_MAX when sending request or no reply 
       */   
      float GetAnalogVoltage(const int aChannel,
			     unsigned int aTimeout_ms);
      
      /**
       * Sets the wanted analog voltage value for the selected channel. (This commands
       * the D/A converted on the IO device)
       *
       * @param[in] aChannel               The channel of analog output
       * @param[in] aVoltage               The voltage value to set.
       * @return                           true on success, false on error (invalid input,
       *                                   or device error)
       */
      bool SetAnalogVoltage(const int aChannel, const double aVoltage);
      
    private:
      /// Thread runner function. Is in charge of receiving events.
      int ThreadFunction(const int aThreadNumber);
      int ProcessMessage(const gimi::GIMIMessage &msg); ///< Incoming data processor function
      /// Flag indicating whether this Client module is Open():ed and running.
      std::map<int,CIOData *> iReplyMessages;
      int iCmdSequence;
    };


 }
}





#endif ///< _MACI_INTERFACE_IOCLIENT_HPP_
